package Raytracing;


import Raytracing.Accelerators.Accelerator;
import Raytracing.Accelerators.KDTree;
import Raytracing.Shaders.SolidShader;
import Raytracing.Geometry.Triangle;
import Raytracing.Shaders.Shader;
import java.util.*;
import SGCamera.*;
import processing.core.*;



public class Raytracer {

    public Accelerator accelerator;
    public ArrayList shaders = new ArrayList();
    public ArrayList baked = new ArrayList();
    public ArrayList lights = new ArrayList();
    PApplet g;
    float bscale = 100.f;
    boolean DEBUG = false;
    boolean enableLighting = false;

    SGCamera camera = null;

    public void setCamera(SGCamera camera) {
        this.camera = camera;
    }

    
    /** Creates a new instance of Raytracer
     * @param g 
     */
    public Raytracer(PApplet g) {
        this(g, new KDTree());
    }

    /** Creates a new instance of Raytracer
     * @param g
     * @param accel 
     */
    public Raytracer(PApplet g, Accelerator accel) {
        this.g = g;
        this.accelerator = accel;
        g.hint(PApplet.ENABLE_ACCURATE_TEXTURES);

    }

    public void add(Intersectable newObject) {
        if (DEBUG) {
            PApplet.println("adding");
        }
        accelerator.add(newObject);
    }

    public void setLighting(boolean enableLighting) {
        this.enableLighting = enableLighting;
    }


    
    public void draw() {

        //draw lights
        if (enableLighting) {
            Lamp lgt;
            for (int i = 0; i < lights.size(); i++) {
                lgt = (Lamp) lights.get(i);
                lgt.draw(g, 10);
                g.pointLight(255, 255, 255, lgt.origin.x, lgt.origin.y, lgt.origin.z);
            }
        }
        
        //draw triangles
        g.noStroke();
        g.textureMode(PApplet.NORMALIZED);
        if (camera == null){
            accelerator.draw(g);        
        }
        else{
            accelerator.draw(g,camera.pos);
        }

    }

    public void openFile(String path) {
        openFile(path, null);
    }

    public void openFile(String path, SGCamera cam) {
        String[] in = g.loadStrings(path);

        try {

            //there should be a shader pack with a .mrb extension
            String path2 = path.substring(0, path.length() - 3);
            path2 = path2 + "mrb";

            StringTokenizer ps = new StringTokenizer(path,"/",true);
            String root = "";
            int tokn = ps.countTokens();
            System.out.println("tokens" + tokn);
            for (int i=0; i < tokn-1;i++){
                root += ps.nextToken();
            }



            System.out.println("Root:" + root);
            if (DEBUG) {
                PApplet.println(path2);
            }
            //replace the three letter char
            try {

                String[] in2 = g.loadStrings(path2);

                //need to load all of the baked textures
                StringTokenizer stk;
                for (int i = 0; i < in2.length; i++) {
                    stk = new StringTokenizer(in2[i], "=[]|,\" ");
                    String tok = stk.nextToken();
                    if (tok.equals("b")) {
                        stk.nextToken();
                        PImage tmp = new PImage();
                        tmp = g.loadImage(root + "/" +  stk.nextToken());

                        baked.add(tmp);
                                    if (DEBUG) {
                PApplet.println("Loaded texture " + tmp);
            }
                    }
                }
            } catch (Exception e) {
                PApplet.println("Couldn't find texture file." + e);
            }

            //in.readLine();   //should be muray string descriptor

            if (DEBUG) {
                System.out.println("Reading input file");
            }
            StringTokenizer stk;
            boolean foundCamTarget = false;
            for (int j = 1; j < in.length; j++) {
                String[] splt = in[j].split("=");

                //parse shaders
                if (splt[0].equals("shad")) {
                    if (DEBUG) {
                        System.out.println("parsing shader");
                    }
                    stk = new StringTokenizer(splt[1], ",[]");
                    float red = Float.parseFloat(stk.nextToken());
                    float green = Float.parseFloat(stk.nextToken());
                    float blue = Float.parseFloat(stk.nextToken());

                    SolidShader tmp = new SolidShader("parsed", g.color(255 * red, 255 * green, 255 * blue), red, green, blue);
                    shaders.add(tmp);
                }
                else if (splt[0].equals("numverts")) {
                    if (DEBUG) {
                        System.out.println("parsing verts");
                    }
                    int numVerts = Integer.parseInt(splt[1]);
                    ArrayList pts = new ArrayList();
                    String line;
                    float x, y, z;
                    for (int i = 0; i < numVerts; i++) {
                        j++;
                        line = in[j];
                        stk = new StringTokenizer(line, "=|");
                        stk.nextToken();    //ditch a v
                        x = -bscale * Float.parseFloat(stk.nextToken());
                        z = bscale * Float.parseFloat(stk.nextToken());
                        y = -bscale * Float.parseFloat(stk.nextToken());

                        pts.add(new PVector(x, y, z));
                    }
                    //we've read in the vertices, now read in the corresponding triangles
                    boolean exit = false;
                    float u0, v0, u1, v1, u2, v2;
                    if (DEBUG) {
                        System.out.println("parsing triangles");
                    }
                    while (exit == false) {
                        j++;
                        line = in[j];
                        stk = new StringTokenizer(line, "=|");

                        if (stk.nextToken().equals("t")) {

                            int whichVert0 = Integer.parseInt(stk.nextToken());
                            int whichVert1 = Integer.parseInt(stk.nextToken());
                            int whichVert2 = Integer.parseInt(stk.nextToken());
                            int whichShader = Integer.parseInt(stk.nextToken());

                            u0 = Float.parseFloat(stk.nextToken());
                            v0 = Float.parseFloat(stk.nextToken());
                            u1 = Float.parseFloat(stk.nextToken());
                            v1 = Float.parseFloat(stk.nextToken());
                            u2 = Float.parseFloat(stk.nextToken());
                            v2 = Float.parseFloat(stk.nextToken());

                            int whichBake = Integer.parseInt(stk.nextToken());
                            Triangle tmp;

                            if (whichBake == -1) {
                                tmp = new Triangle((Shader) shaders.get(whichShader),
                                        (PVector) pts.get(whichVert0),
                                        (PVector) pts.get(whichVert1),
                                        (PVector) pts.get(whichVert2),
                                        u0, v0, u1, v1, u2, v2, null);

                            }
                            else {
                                tmp = new Triangle((Shader) shaders.get(whichShader),
                                        (PVector) pts.get(whichVert0),
                                        (PVector) pts.get(whichVert1),
                                        (PVector) pts.get(whichVert2),
                                        u0, v0, u1, v1, u2, v2, (PImage) baked.get(whichBake));
                            }

                            accelerator.add(tmp);

                        }
                        else {

                            exit = true;
                            //shouldn't have read this line
                            j--;
                        }
                    }

                }
                else if (splt[0].equals("cam")) {
                    System.out.println("Found Camera");
                    if (cam != null) {
                        stk = new StringTokenizer(splt[1], ",[]|");

                        float type = Float.parseFloat(stk.nextToken());

                        float rx = Float.parseFloat(stk.nextToken());
                        float ry = Float.parseFloat(stk.nextToken());
                        float rz = Float.parseFloat(stk.nextToken());

                        float ux = Float.parseFloat(stk.nextToken());
                        float uy = Float.parseFloat(stk.nextToken());
                        float uz = Float.parseFloat(stk.nextToken());

                        float dx = -Float.parseFloat(stk.nextToken());
                        float dz = Float.parseFloat(stk.nextToken());
                        float dy = -Float.parseFloat(stk.nextToken());

                        //convert from Blender coords to Processing coords

                        float cx = -bscale * Float.parseFloat(stk.nextToken());
                        float cz = bscale * Float.parseFloat(stk.nextToken());
                        float cy = -bscale * Float.parseFloat(stk.nextToken());


                        cam.setPosition(cx, cy, cz);
                        //if (!foundCamTarget) cam.lookAt(cx + dx, cy + dy, cz + dz);
                        if (!foundCamTarget) cam.lookAt(0,0,0);
                        cam.setUpVector(ux, uy, uz);
                    }
                }

                else if (splt[0].equals("camTarget")) {
                    System.out.println("Found Camera Target");
                    if (cam != null) {
                        stk = new StringTokenizer(splt[1], ",[]|");

                        float cx = -bscale * Float.parseFloat(stk.nextToken());
                        float cz = bscale * Float.parseFloat(stk.nextToken());
                        float cy = -bscale * Float.parseFloat(stk.nextToken());

                        cam.lookAt(cx, cy, cz);
                        foundCamTarget = true;
                        System.out.println(cam.target);
                    }
                }



                else if (splt[0].equals("lamp")) {
                    stk = new StringTokenizer(splt[1], ",[]()|");

                    float lx = -bscale * Float.parseFloat(stk.nextToken());
                    float lz = bscale * Float.parseFloat(stk.nextToken());
                    float ly = -bscale * Float.parseFloat(stk.nextToken());
                    //ugh ... need to work light handling into the root proggie :9
                    lights.add(new Lamp(new PVector (lx, ly, lz), 255));
                }
            }
        } catch (Exception e) {
            PApplet.println("Error while parsing triangle " + e);
        }
        accelerator.validate();
    }
}
